#ifndef INC_UMLDIAGRAM_H
#define INC_UMLDIAGRAM_H

#include "../base/Observable.h"
#include "../base/Observer.h"
#include "UMLObject.h"  // not strictly necessary, but convenient

#include <vector>
#include <set>
#include <QSize>
#include <QObject>

using std::vector;
using std::set;

/**
 * Essentially a "smart" container for UMLObjects, and therefore uses the STL
 * container class interface, for better or for worse.
 *
 * I finally broke down and gave this thing signals.
 */
class UMLDiagram : public QObject, public Observable, Observer
{
	Q_OBJECT;

	public:

		/* typedefs for UMLObjects "active" in the diagram */
		typedef vector<UMLObject*> active_type;
		typedef active_type::iterator iterator;
		typedef active_type::const_iterator const_iterator;
		typedef active_type::reverse_iterator reverse_iterator;
		typedef active_type::const_reverse_iterator const_reverse_iterator;

		/* typedefs for remembering all UMLObjects ever known */
		typedef set<UMLObject*> known_type;

		UMLDiagram();
		UMLDiagram(const UMLDiagram&); // rule of three
		~UMLDiagram();

		/*** Accessors ***/

		/**
		 * @return the position of the farthest corner of the farther contained
		 * UMLObject from the origin.
		 */
		QSize size() const;

		/**
		 * Returns an iterator over the collection of UMLObjects on the diagram,
		 * from first-added to last-added. Iterator dereferences to a
		 * (UMLObject*).
		 */
		iterator begin();
		const_iterator begin() const;

		/**
		 * Returns the corresponding end iterator to begin().
		 */
		iterator end();
		const_iterator end() const;

		reverse_iterator rbegin();
		const_reverse_iterator rbegin() const;

		reverse_iterator rend();
		const_reverse_iterator rend() const;

		/*** Operators ***/

		UMLDiagram& operator=(const UMLDiagram&);

		/**
		 * Adds a copy of the given UMLObject to the diagram.
		 *
		 * CAVEAT EMPTOR: Note that if the given UMLObject has previously been
		 * part of the diagram, the actual object and not a copy will be used.
		 * This is for undo/redo convenience.
		 */
		UMLObject& add(UMLObject&);

		/**
		 * Removes the UMLObject specified by the iterator from the diagram. As
		 * noted above, the diagram never "owns" its UMLObjects, so the caller
		 * is responsible for freeing the object when appropriate.
		 */
		void remove(iterator);

		vector<UMLObject> neighbors(const UMLObject&);

		// TODO: make this private
		void update(Observable* o);

	signals:
		void objectRemoved(UMLObject*);

	private:
		active_type _active;
		known_type _known;
		QSize _size;
};

inline UMLDiagram::iterator UMLDiagram::begin()
{
	// Note: returning iterators means clients can modify UMLObjects, which is
	// desired functionality. Clients cannot, however, modify the internal
	// collection, which is also desired funcationality.
	return _active.begin();
}

inline UMLDiagram::const_iterator UMLDiagram::begin() const
{
	return _active.begin();
}

inline UMLDiagram::iterator UMLDiagram::end()
{
	// See note on begin().
	return _active.end();
}

inline UMLDiagram::const_iterator UMLDiagram::end() const
{
	return _active.end();
}

inline UMLDiagram::reverse_iterator UMLDiagram::rbegin()
{
	// See note on begin().
	return _active.rbegin();
}

inline UMLDiagram::const_reverse_iterator UMLDiagram::rbegin() const
{
	return _active.rbegin();
}

inline UMLDiagram::reverse_iterator UMLDiagram::rend()
{
	// See note on begin().
	return _active.rend();
}

inline UMLDiagram::const_reverse_iterator UMLDiagram::rend() const
{
	return _active.rend();
}

#endif // INC_UMLDIAGRAM_H

